<?php
/**
 * 模板类
 * @copyright reginx.com
 * $Id: tpl.class.php 10 2013-01-24 09:42:23Z reginx $
 */
class tpl {
    /**
     * 值
     *
     * @var unknown_type
     */
    private $_val    = array();

    /**
     * 配置
     *
     * @var unknown_type
     */
    private $_conf = array();

    private $_curm = '';
    /**
     * 架构函数
     *
     * @param unknown_type $param
     */
    private  function __construct($app){
        $this->_conf = $app;
        $this->_conf['out_dir']  = $app['temp_path'] . $app['app_dir'];
        $this->_conf['tpl_dir']  = $app['app_tpl_path'] .$app['tpl'].'/';
        if(!is_dir($this->_conf['out_dir'])){
            core::makedir($this->_conf['out_dir']);
        }
    }


    /**
     * 获取模板对象
     *
     * @param String $out_dir
     * @param mixed $param
     * @return Object
     */
    static function getTpl($conf , $param=array()){
        static $tplobj = NULL;
        if(empty($tplobj)){
            $tplobj = new tpl($conf ,$param);
        }
        return $tplobj;
    }

    /**
     * 模板赋值
     *
     * @param unknown_type $k
     * @param unknown_type $v
     */
    function assign($k , $v){
        $this->_val[$k] = $v;
    }

    /**
     * 输出模板
     *
     * @param unknown_type $tplFile
     */
    function display($tplfile , $doctype='text/html'){
        $tplfile = $this->parsetpl_dir($tplfile);
        echo($this->fetch($tplfile,$this->_conf['charset'],$doctype,true));
        exit();
    }

    /**
     * 解析引用的模板文件DIR
     * @example user@default:index.html
     *          App @ style : tpl file
     * @param unknown_type $tplfile
     */
    function parsetpl_dir($tplfile){
        if(preg_match('/^(.*?)@(.*?)\:(.*?)$/',$tplfile)){
            preg_match('/^(.*?)@(.*?)\:(.*?)$/',$tplfile,$tmp);
            $this->_conf['tpl_dir'] = $this->_conf['base_path'] . ($tmp[1] == '' ? '' : ($tmp[1] . '/' )).'template/'.
            ($tmp[2] == '' ? 'default/' : ($tmp[2].'/'));
            return $tmp[3];
        }
        return $tplfile;
    }

    /**
     * 设置模板文件目录
     *
     * @param unknown_type $dir
     */
    public function settpldir($dir){
        $this->_conf['tpl_dir'] = $dir;
    }

    /**
     * 获取页面html内容
     *
     * @param unknown_type $tplFile
     * @param unknown_type $charset
     * @param unknown_type $contentType
     * @param unknown_type $display
     * @return unknown
     */
    public function &fetch($sfile,$charset=null,$contentType='text/html',$display=false){
        $tplfile = $this->_conf['tpl_dir'].$sfile;
        $tmpfile   = $this->_conf['out_dir'] . $this->_getcachetpl($sfile);
        !is_dir(dirname($tmpfile)) && core::makeDir(dirname($tmpfile));
        !file_exists($tplfile) && core::error(core::L('not-found',str_replace(BASE_PATH,'',$tplfile)),'Tpl::fetch',1);
        if(empty($charset)){
            $charset = $GLOBALS['_APP']['charset'];
        }
        // 非Debug模式下. 清空一切非模板输出
        !IS_DEBUG && ob_end_clean();
        ob_start();
        ob_implicit_flush(0);
        if($display){
            header('X-Powered-By: REGINX v'.REGINX_VERSION);
            header("Content-Type: ".$contentType."; charset=".$charset);
        }
        extract($this->_val);
        !$this->_available($tmpfile,$tplfile) && $this->_parsetpl($tmpfile,$tplfile);
        include_once($tmpfile);
        $content = ob_get_clean();
        return $content;
    }

    /**
     * 编译模板
     *
     */
    private function _parsetpl($tempfile,$tplfile){
        $rule = array(
            array(
                0   => '/\{\$([^\|]*?)\}/i',
                1   => '/\{\s?foreach\s+\$?(.*?)\s+\$?(.*?)\s+\$?(.*?)\s*\}/i',
                2   => '/\{\/foreach\s*\}/i',
                3   => '/\{\s?if\s*?(.+?)\s*\}/i',
                4   => '/\{\s?else\s*if(.*?)\s?\}/i',
                5   => '/\{\s?else\s?\s*\}/i',
                6   => '/\{\/if\s*?\}\s*/i',
                7   => '/\{\s?__([\w\-_]*?)__\s?\}/ies',
                8   => '/\{\s?for\s+\$?(.*?)\s+in\s+range\(\s*([^\s\,]*?)\s*,\s*(.*?)\)\s*\}/i',
                9   => '/\{\s?for\s+\$?(.*?)\s+in\s+range\(\s*([^\s\,]*?)\s*,\s*([^\s\,]*?)\s*,\s*([^\s]*?)\s*\)\s*\}/i',
                10  => '/\{\/for\\s*}/i',
                11  => '/\{lang\:(.*?)\}/ies',
                12  => '/\{\s?:(.*?)\s*?\}/i',
                13  => '/\{\s?\$(.*?)\|(\d+)\s*?\}/i',
                14  => '/\{\s?:(.*?)\s*?\;?\s*?\}/i',
                15  => '/\{\s*url\:\(?(.*?)\)?\}/i',
                16  => '/\{\s?\@TIME(.*?)\s*?\}/i',
                17  => '/\{\s?\$(.*?)\|html\s*?\}/i',
                18  => '/\{\s?\$(.*?)\|thumb\s*?\}/i',
                19  => '/\{\s?_([\w\-_]*?)_\s?\}/is',
            ),
            array(
                0   => '<?php echo($\1);?>',
                1   => '<?php if(is_array($\1)): $\2_index = 0;foreach($\1 as $\2 => $\3):$\2_index++;?>',
                2   => '<?php endforeach;endif;?> ',
                3   => '<?php if(\1):?>',
                4   => '<?php elseif(\1):?>',
                5   => '<?php else:?>',
                6   => '<?php endif;?>',
                7   => 'isset($GLOBALS["_APP"][strtolower("\1")]) ? $GLOBALS["_APP"][strtolower("\1")] : \1',
                8   => '<?php for($\1=\2;$\1<\3;$\1++):?>',
                9   => '<?php for($\1=\2;(\3>\2 ? ($\1<\3) : ($\1>\3));$\1+=\4):?>',
                10  => '<?php endfor;?> ',
                11  => "\$this->lang('\\1')",
                12  => '<?php echo(\1);?>',
                13  => '<?php echo(core::msubstr($\1,0,\2));?>',
                14  => '<?php echo(\1);?>',
                15  => "<?php echo(core::url(\\1)); ?>",
                16  => '<?php printf("%.3f",(microtime(TRUE) - $GLOBALS["_MISC"]["STIME"])\1); ?>',
                17  => '<?php echo(htmlspecialchars_decode($\1)); ?>',
                18  => '<?php echo(core::thumburl($\1)); ?>',
                19  => '<?php echo(isset($GLOBALS["_APP"][strtolower("\1")]) ? $GLOBALS["_APP"][strtolower("\1")] : \1); ?>',
            ),
        );
        $html = file_get_contents($tplfile);
        $html = preg_replace('/\s*\<\!\-\-'.preg_quote($GLOBALS['_APP']['tpl_pre']).'\s*(.+?)\s*'.preg_quote($GLOBALS['_APP']['tpl_suf']).'\-\-\>\s*/i','{\\1}',$html);
        $this->_getinctpl($html);
        $html = preg_replace($rule[0],$rule[1],$html);
        $html = preg_replace('/(\s)+\<\?php(.*?)\?\>/i', "\\1<?php\\2?>", $html);
        $html = preg_replace('/\<\?php(.*?)\?\>(\s)+/i', "<?php\\1?>\\2", $html);
        $html = "<?php !defined('IN_REGINX') && exit('Access Denied');?>\r\n" . $html;
        file_put_contents($tempfile,$html);
        $html = NULL;unset($html);
    }


    /**
     * 处理 Include 语句
     *
     * 支持包含其他App模板文件
     * {include admin@default:top.html} 则包含admin应用下default样式下的top.html
     * {include @style1:top.html} 包含主应用下的style1目录下的top.html
     * {include 应用ID@样式名称:模板文件名称}
     * @abstract
     * @param unknown_type $html
     */
    private function _getinctpl(&$html,$limit=1){
        preg_match_all('/\{\s*?include\s+(.*?)\s*?\}/i',$html,$inc);
        for($j=0;$j<sizeof($inc[1]);$j++){
            $incfile = $this->_conf['tpl_dir'].$inc[1][$j];
            if(preg_match("/^(.*?)@.*?$/i",$inc[1][$j])){
                preg_match('/^(.*?)@(.*?)\:(.*?)$/',$inc[1][$j],$tmp);
                $incFile = $GLOBALS['_APP']['base_path'].($tmp[1] == ''?'':($tmp[1].'/')).'template/'.
                ($tmp[2] == '' ? 'default/':($tmp[2].'/')).$tmp[3];
            }
            !file_exists($incfile) && core::error(core::L('not-found',$incfile),'Tpl::getIncTpl',1);
            $s = str_replace('/','\/',str_replace('\\','/',$inc[1][$j]));
            $html = preg_replace('/\{\s*?include\s+'.$s.'\s*?\}/is',file_get_contents($incfile),$html);
        }
        /**  支持最多3层 **/
        if(preg_match('/\{\s*?include\s+(.*?)\s*?\}/i',$html) && $limit <= 3){
            $this->_getinctpl($html,$limit+1);
        }
    }

    /**
     * 检查缓存模板文件是否可用
     *
     * @param unknown_type $tmpfile
     * @param unknown_type $sfile
     * @return unknown
     */
    private function _available($tmpfile,$sfile){
        $ret = true;
        if(!file_exists($tmpfile)){
            $ret = false;
        }else{
            if($GLOBALS['_APP']['debug']){
                $ret = false;
            }elseif($GLOBALS['_APP']['tpl_ttl']){
                $ret = filemtime($tmpfile) > filemtime($sfile);
            }
        }
        return $ret;
    }

    /**
     * 获取缓存模板文件名称
     *
     * @param unknown_type $key
     * @return unknown
     */
    private function _getcachetpl($key){
        $mod = $this->_curm ? $this->_curm : $GLOBALS['_MOD'];
        $key = $GLOBALS['_APP']['app_name'].'_'.$GLOBALS['_APP']['lang'] . '_' . $mod . '_' . $this->_conf['tpl'] . $key;
        return sprintf('%X', crc32($key)).'.php';
    }

    /**
     * 设置当前输出模板所属mod
     *
     * @param unknown_type $mod
     */
    public function setcurm($mod=false){
        $this->_curm = $mod;
    }

    /**
     * 语言解析
     *
     */
    public function lang($args){
        $keys = is_array($args) ? $args : explode(',' , preg_replace('/[\s|\'|\"]*/e' , '' , $args));
        if(!empty($keys)){
            for ($i=0; $i<sizeof($keys); $i++){
                if(!empty($keys[$i]) && $keys[$i] != ''){
                    $keys[$i] = isset($GLOBALS['_LANG'][$keys[$i]])?$GLOBALS['_LANG'][$keys[$i]]:$keys[$i];
                }
            }
            $format = array_shift($keys);
            $flen = sizeof(preg_split('/\%\w/i' , $format));
            while(sizeof($keys) < $flen){ // 使数组元素总数与占位符数目相对应,否则会报错
                $keys[] = '';
            }
            return  vsprintf($format , $keys);
        }
    }

}
?>